package org.playground.booking.service.impl;

import org.apache.log4j.Logger;
import org.playground.booking.dao.IBookingDao;
import org.playground.booking.dao.IBookingStatusDao;
import org.playground.booking.dao.IPlaygroundDao;
import org.playground.booking.dao.IUserDao;
import org.playground.booking.dto.BookingDto;
import org.playground.booking.dto.CoursDto;
import org.playground.booking.entity.Booking;
import org.playground.booking.entity.BookingStatus;
import org.playground.booking.entity.Playground;
import org.playground.booking.entity.User;
import org.playground.booking.enums.BookingStatusEnum;
import org.playground.booking.service.IBookingService;
import org.playground.booking.utils.CommonUtils;
import org.playground.booking.utils.PlaygroundBookingErrMsg;
import org.playground.booking.utils.PlaygroundBookingException;
import org.playground.booking.utils.UnknownResourceException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

@Service
public class BookingServiceImpl implements IBookingService {
    private static final Logger LOGGER = Logger.getLogger(BookingServiceImpl.class);


    @Autowired
    private IBookingDao bookingDao;
    @Autowired
    private IPlaygroundDao playgroundDao;
    @Autowired
    private IUserDao userDao;
    @Autowired
    private IBookingStatusDao bookingStatusDao;


    public Long createBooking(BookingDto booking) throws PlaygroundBookingException {


        if(!checkBookingValidation(booking)){
            LOGGER.error("Playground or its children playground has been already booked");
            throw new PlaygroundBookingException(PlaygroundBookingErrMsg.BOOKING_ALREADY_EXIST);
        }
        //getuser
        Long userId = booking.getUserId();
        User user = userDao.readById(userId);
        if(user==null){
            LOGGER.error(new String(PlaygroundBookingErrMsg.UNLOGGED_USER.getErrorMessage()));
            throw new PlaygroundBookingException(PlaygroundBookingErrMsg.UNLOGGED_USER);
        }
        //get playground
        if(booking.getCours()==null){

            LOGGER.error(PlaygroundBookingErrMsg.COURS_NOT_SELECTED.getErrorMessage());
            throw new PlaygroundBookingException(PlaygroundBookingErrMsg.COURS_NOT_SELECTED);
        }
        Long pgId = booking.getCours().getCoursId();

        Playground pg = playgroundDao.readById(pgId);

        List<Booking> bookingsToRecord = new ArrayList<Booking>();

        Booking bookingRecord = new Booking();
        bookingRecord.setStartFrom(booking.getStart());
        bookingRecord.setEndAt(booking.getEnd());
        bookingRecord.setPlayground(pg);
        bookingRecord.setUser(user);
        //CREATED
        BookingStatus bookingStatus = bookingStatusDao.readBookingStatusByValue(BookingStatusEnum.CREATED);
        bookingRecord.setBookingStatus(bookingStatus);
        bookingsToRecord.add(bookingRecord);

        //calculate booking
        //if big pg is booked then all small pg should be booked


        BookingStatus bookingStatusLinked = bookingStatusDao.readBookingStatusByValue(BookingStatusEnum.LINKED_BOOKING);
        if(pg.getChildPlaygrounds()!=null&&pg.getChildPlaygrounds().size()>0){
            for(Playground child : pg.getChildPlaygrounds()){
                Booking childRecord = new Booking();
                childRecord.setStartFrom(booking.getStart());
                childRecord.setEndAt(booking.getEnd());
                childRecord.setPlayground(child);
                childRecord.setUser(user);
                //LINKED
                childRecord.setBookingStatus(bookingStatusLinked);
                bookingsToRecord.add(childRecord);

            }
        }
        if(pg.getParentPlayground()!=null){
            Booking parentRecord = new Booking();
            parentRecord.setStartFrom(booking.getStart());
            parentRecord.setEndAt(booking.getEnd());
            parentRecord.setPlayground(pg.getParentPlayground());
            parentRecord.setUser(user);
            //LINKED
            parentRecord.setBookingStatus(bookingStatusLinked);
            bookingsToRecord.add(parentRecord);
        }

        //calculate price
        //TODO next version

        //save bookings
        for(Booking toRecord : bookingsToRecord){
            bookingDao.save(toRecord);
        }

        return bookingRecord.getId();


    }

    public void updateBooking(BookingDto booking) throws PlaygroundBookingException {
        Long bookingId = booking.getBookingId();
        Booking toUpdate = bookingDao.readById(bookingId);
        if(toUpdate==null){
            LOGGER.error(String.format(PlaygroundBookingErrMsg.BOOKING_NOT_FOUND.getErrorMessage(),bookingId));
            throw new PlaygroundBookingException(PlaygroundBookingErrMsg.BOOKING_NOT_FOUND.getErrorCode()+" : "+
                    String.format(PlaygroundBookingErrMsg.BOOKING_NOT_FOUND.getErrorMessage(),bookingId));
        }else{
            BookingStatusEnum statusEnum = booking.getBookingStatus();
            //get booking status from database
            BookingStatus status = bookingStatusDao.readBookingStatusByValue(statusEnum);
            //BookingStatus status = booking
            if(status!=null){
                toUpdate.setBookingStatus(status);
            }
            toUpdate.setStartFrom(booking.getStart());
            toUpdate.setEndAt(booking.getEnd());
            bookingDao.update(toUpdate);
        }
    }

    public List<BookingDto> getBookingList(Long userId, int pageNb, int pageSize, boolean includeExpired){
        List<Booking> bookings;

        if(includeExpired){
            bookings = bookingDao.readBookingForUser(userId,pageNb,pageSize,BookingStatusEnum.CREATED,
                    BookingStatusEnum.WAITING_FOR_PAYMENT,
                    BookingStatusEnum.SUBMITTED,
                    BookingStatusEnum.CANCELLED,
                    BookingStatusEnum.REFUNDED);
        }else {
            bookings = bookingDao.readBookingForUser(userId, pageNb,pageSize, BookingStatusEnum.CREATED,
                    BookingStatusEnum.WAITING_FOR_PAYMENT,
                    BookingStatusEnum.SUBMITTED);
        }

        ArrayList<BookingDto> bookingDtos = new ArrayList<BookingDto>();

        for(Booking booking : bookings){
            BookingDto bookingDto = new BookingDto();
            bookingDto.setStart(booking.getStartFrom());
            bookingDto.setEnd(booking.getEndAt());
            bookingDto.setUserId(booking.getUser()==null?null:booking.getUser().getId());
            bookingDto.setBookingId(booking.getId());
            CoursDto coursDto = new CoursDto();
            coursDto.setCoursId(booking.getPlayground()==null?null:booking.getPlayground().getId());
            coursDto.setName(booking.getPlayground()==null?null:booking.getPlayground().getPlaygroundName());
            bookingDto.setCours(coursDto);
            bookingDtos.add(bookingDto);
        }

        return bookingDtos;

    }


    public BookingDto getBooking(Long userId, Long id) throws UnknownResourceException {
        Booking booking = bookingDao.readBookingForUser(userId,id);
        if(booking!=null){
            BookingDto bookingDto = new BookingDto();
            bookingDto.setStart(booking.getStartFrom());
            bookingDto.setEnd(booking.getEndAt());
            bookingDto.setUserId(booking.getUser()==null?null:booking.getUser().getId());
            bookingDto.setBookingId(booking.getId());
            CoursDto coursDto = new CoursDto();
            coursDto.setCoursId(booking.getPlayground()==null?null:booking.getPlayground().getId());
            coursDto.setName(booking.getPlayground()==null?null:booking.getPlayground().getPlaygroundName());
            bookingDto.setCours(coursDto);
            return bookingDto;
        }else{
            throw new UnknownResourceException();
        }
    }

    @Override
    public List<BookingDto> getBookingList(Long merchantId, Long sportId, Long courtId, Date startDate, Date endDate, Integer limit, Integer pageIndex)throws IllegalArgumentException {
        if(merchantId==null||merchantId==0) {
            throw new IllegalArgumentException("The merchant Id is not specified");
        }
        if(limit==null||limit<=0||limit>CommonUtils.PAGING_MAX_LIMIT){
            limit=CommonUtils.PAGING_MAX_LIMIT;
        }
        if(endDate==null){
            endDate = new Date();
        }
        if(startDate==null){
            Calendar cal = Calendar.getInstance();
            cal.set(1990,1,1);
            startDate=cal.getTime();
        }
        if(pageIndex==null){pageIndex=0;}


        List<Booking> bookings = bookingDao.getBookingsForPlayground(courtId,startDate,endDate, limit, pageIndex);
        ArrayList<BookingDto> bookingDtos = new ArrayList<BookingDto>();

        for(Booking booking : bookings){
            BookingDto bookingDto = new BookingDto();
            bookingDto.setStart(booking.getStartFrom());
            bookingDto.setEnd(booking.getEndAt());
            bookingDto.setUserId(booking.getUser()==null?null:booking.getUser().getId());
            bookingDto.setBookingId(booking.getId());
            CoursDto coursDto = new CoursDto();
            coursDto.setCoursId(booking.getPlayground()==null?null:booking.getPlayground().getId());
            coursDto.setName(booking.getPlayground()==null?null:booking.getPlayground().getPlaygroundName());
            bookingDto.setCours(coursDto);
            bookingDtos.add(bookingDto);
        }

        return bookingDtos;
    }

    public boolean checkBookingValidation(BookingDto bookingDto){
        Long pgId = bookingDto.getCours().getCoursId();
        Date start = bookingDto.getStart();
        Date end = bookingDto.getEnd();

        return !bookingDao.playgroundAlreadyBooked(pgId,start,end);

    }
}
